package cn.gs.util.set;

// @author gs
import com.sleepycat.bind.EntryBinding;
import com.sleepycat.bind.serial.SerialBinding;
import com.sleepycat.bind.serial.StoredClassCatalog;
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.collections.StoredSortedMap;
import com.sleepycat.je.*;
import java.io.File;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class BDBMap<K, V> extends AbstractMap<K, V> {

    static StoredClassCatalog classCatalog;

    static StoredClassCatalog getClassCatalog(Environment env) {
        if (classCatalog == null) {
            DatabaseConfig dbConfig = new DatabaseConfig();
            dbConfig.setAllowCreate(true);
            try {
                Database classCatalogDB = env.openDatabase(null, "classCatalog", dbConfig);
                classCatalog = new StoredClassCatalog(classCatalogDB);
            } catch (DatabaseException e) {
                // TODO Auto-generated catch block  
                throw new RuntimeException(e);
            }
        }
        return classCatalog;
    }

    /**
     * 创建以及绑定数据库
     *
     * @param dbDir
     * @param dbName
     * @param valueClass
     * @throws DatabaseNotFoundException
     * @throws DatabaseExistsException
     * @throws DatabaseException
     * @throws IllegalArgumentException
     */
    public static <K, V> BDBMap<K, V> createAndBindDatabase(String dbDir, String dbName, Class<K> keyClass, Class<V> valueClass) throws DatabaseNotFoundException,
            DatabaseExistsException, DatabaseException, IllegalArgumentException {
        File envFile = null;
        EnvironmentConfig envConfig = null;
        DatabaseConfig dbConfig = null;
        Database db = null;

        try {
            // 数据库位置  
            envFile = new File(dbDir);

            // 数据库环境配置  
            envConfig = new EnvironmentConfig();
            envConfig.setAllowCreate(true);
            envConfig.setTransactional(false);
            envConfig.setDurability(Durability.COMMIT_SYNC);
            // 数据库配置  
            dbConfig = new DatabaseConfig();
            dbConfig.setAllowCreate(true);
            dbConfig.setTransactional(false);
            dbConfig.setDeferredWrite(false);

            // 创建环境  
            BdbEnvironment dbEnv = new BdbEnvironment(envFile, envConfig);
            // 打开数据库  
            db = dbEnv.openDatabase(null, dbName, dbConfig);
            // 绑定数据库 
            return bindDatabase(db, keyClass, valueClass, dbEnv.getClassCatalog());

        } catch (DatabaseNotFoundException e) {
            throw e;
        } catch (DatabaseExistsException e) {
            throw e;
        } catch (DatabaseException e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw e;
        }
    }

    /**
     * 绑定数据库
     *
     * @param db
     * @param valueClass
     * @param classCatalog
     */
    public static <K, V> BDBMap<K, V> bindDatabase(Database db, Class<K> keyClass, Class<V> valueClass, StoredClassCatalog classCatalog) {
        StoredSortedMap<K, V> queueMap = new StoredSortedMap<K, V>(
                db, // db  
                TupleBinding.getPrimitiveBinding(keyClass), //Key  
                TupleBinding.getPrimitiveBinding(valueClass), //Key  
                true);
        return new BDBMap(queueMap);
    }

    public static <K, V> BDBMap<K, V> create(File dir, String name, Class<K> k, Class<V> v) {
        EnvironmentConfig environmentConfig = new EnvironmentConfig();
        environmentConfig.setTransactional(true);
        environmentConfig.setAllowCreate(true);
        environmentConfig.setDurability(Durability.COMMIT_WRITE_NO_SYNC);
        Environment environment = new Environment(dir.getAbsoluteFile(), environmentConfig);

        DatabaseConfig myDbConfig = new DatabaseConfig();
        myDbConfig.setAllowCreate(true);
        myDbConfig.setTransactional(true);
        myDbConfig.setSortedDuplicates(true);

        // Set the Berkeley DB config for opening all stores.  
        DatabaseConfig dbConfig = new DatabaseConfig();
        dbConfig.setTransactional(true);
        dbConfig.setAllowCreate(true);

        Database db = environment.openDatabase(null, name, dbConfig);
        // Open the Berkeley DB database for the part, supplier and shipment  
        // stores.  The stores are opened with no duplicate keys allowed.  

        EntryBinding messageKeyBinding =
                new SerialBinding(getClassCatalog(environment), k);
        EntryBinding messageValueBinding =
                new SerialBinding(getClassCatalog(environment), v);

        StoredSortedMap<K, V> messageMap = new StoredSortedMap(db, messageKeyBinding, messageValueBinding, true);
        // Create transactionRunner for the transactional operation  
        return new BDBMap(messageMap);

    }
    StoredSortedMap<K, V> map;

    private BDBMap(StoredSortedMap<K, V> map) {
        this.map = map;
    }

    @Override
    public V get(Object k) {
        return map.get(k);
    }

    @Override
    public V put(K k, V v) {
        return map.put(k, v);
    }

    public V putOrGet(K k, V v) {
        if (map.containsKey(k)) {
            return map.get(k);
        } else {
            map.put(k, v);
            return v;
        }
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        return map.entrySet();
    }
    
    public K firstKey() {
        return map.firstKey();
    }
}
